home *** CD-ROM | disk | FTP | other *** search
/ ETO Development Tools 1 / ETO Development Tools 1.iso / Essentials / Developer Essentials Jul 90 / DTS Sample Code / Macintosh Sample Code / SC.011.GetZoneList / GetZoneList.p < prev    next >
Encoding:
Text File  |  1990-05-01  |  23.0 KB  |  849 lines  |  [TEXT/MPS ]

  1. {------------------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    AppleTalk GetZoneList Sample Application
  6. #
  7. #    GetZoneList
  8. #
  9. #    GetZoneList.p    -    Pascal Source
  10. #
  11. #    Copyright © 1988-90 Apple Computer, Inc.
  12. #    All rights reserved.
  13. #
  14. #    Versions:    1.0                    November 1988
  15. #                1.1                    October 1989
  16. #                1.2                    May 1990
  17. #
  18. #    Components:    GetZoneList.p        May 1, 1990
  19. #                GetZoneList.r        May 1, 1990
  20. #                MakeFile            May 1, 1990
  21. #                UFailure.a            November 1, 1988
  22. #                UFailure.inc1.p        November 1, 1988
  23. #                UFailure.p            November 1, 1988
  24. #
  25. #    GetZoneList is a sample application that uses
  26. #    AppleTalk ATP and ZIP to obtain a list of zones
  27. #    on an AppleTalk internet.
  28. #
  29. #    GetZoneList also demonstrates using a signal, or
  30. #    failure-catching mechanism to recover from error
  31. #    situations.
  32. #
  33. #    GetZoneList is based on DTS Sample.p. For more
  34. #    description and explanantion of the non-example
  35. #    specific areas of this application, please refer to
  36. #    either Sample.p or TESample.p.
  37. #
  38. ------------------------------------------------------------------------------}
  39.  
  40. PROGRAM GetZoneList;
  41.  
  42. USES
  43.     MemTypes, QuickDraw, OSIntf, ToolIntf, AppleTalk, PackIntf, FixMath, Script, UFailure;
  44.  
  45. TYPE
  46. {offsets for xCall queue elements.  See Technical Note #250.}
  47.  
  48. xCallParam = PACKED RECORD
  49.     qLink:QElemPtr;
  50.     qType:INTEGER;
  51.     ioTrap:INTEGER;
  52.     ioCmdAddr:Ptr;
  53.     ioCompletion:ProcPtr;
  54.     ioResult:OSErr;
  55.     ioNamePtr:StringPtr;
  56.     ioVRefNum:INTEGER;
  57.     ioRefNum:INTEGER;
  58.     csCode:INTEGER;
  59.     xppSubCode:INTEGER;
  60.     xppTimeOut:Byte;
  61.     xppRetry:Byte;
  62.     filler:INTEGER;
  63.     zipBuffPtr:Ptr;
  64.     zipNumZones:INTEGER;
  65.     zipLastFlag:INTEGER;
  66.     zipInfoField:  PACKED ARRAY [1..70] of Byte;
  67. END;
  68. xCallParamBlkPtr = ^xCallParam;
  69.  
  70.  
  71. CONST
  72.     _WaitNextEvent            = $A860;
  73.     _Unimplemented            = $A89F;
  74.     kSysEnvironsVersion        = 1;
  75.     kOSEvent                = app4Evt;    {event used by MultiFinder}
  76.     kSuspendResumeMessage    = 1;        {high byte of suspend/resume event message}
  77.     kResumeMask                = 1;        {bit of message field for resume vs. suspend}
  78.  
  79.     kCR                = 13;                {carriage return character}
  80.     kENTER            = 3;                {enter character}
  81.     kScrollBarWidth    = 15;                {the width of the scrollbar in the list}
  82.     kListInset        = -1;                {adjustment for list frame}
  83.     kATPTimeOutVal    = 3;                {re-try ATP SendRequest every 3 seconds}
  84.     kATPRetryCount    = 5;                {for five times}
  85.     kZonesSize        = 578;                {size of buffer for zone names}
  86.     kGZLCall        = $08000000;        {GetZoneList indicator}
  87.     kZIPSocket        = 6;                {the Zone Information Protocol socket}
  88.     kMoreZones        = $FF000000;        {mask to see if more zones to come}
  89.     kZoneCount        = $0000FFFF;        {mask to count zones in buffer}
  90.     kHilite            = 1;                {hilite value for button control}
  91.     kDeHilite        = 0;                {dehilite value for button control}
  92.     kHiliteDelay    = 8;                {time in ticks to leave button hilited}
  93.  
  94.     kMinHeap        = (29 * 1024);
  95.     kMinSpace        = (20 * 1024);
  96.  
  97.     sErrStrings        = 128;                {error string STR#}
  98.     eStandardErr    = 1;
  99.     eWrongMachine    = 2;
  100.     eSmallSize        = 3;
  101.     eNoMemory        = 4;
  102.     eAppleTalk        = 5;
  103.     eNoZones        = 6;
  104.  
  105.     rAboutAlert        = 128;                {about alert}
  106.     rZoneDialog        = 129;                {zone list dialog}
  107.     dZoneList        = 2;                {user item that is zone list}
  108.     dDefault        = 3;                {user item that is default indicator}
  109.     rUserAlert        = 130;                {error alert}
  110.  
  111.     rMenuBar        = 128;                {application's menu bar}
  112.  
  113.     mApple            = 128;                {Apple menu}
  114.     iAbout            = 1;
  115.  
  116.     mFile            = 129;                {File menu}
  117.     iNew            = 1;
  118.     iClose            = 4;
  119.     iQuit            = 12;
  120.  
  121.     mEdit            = 130;                {Edit menu}
  122.     iUndo            = 1;
  123.     iCut            = 3;
  124.     iCopy            = 4;
  125.     iPaste            = 5;
  126.     iClear            = 6;
  127.  
  128.     {1.01 - kDITop and kDILeft are used to locate the Disk Initialization dialogs.}
  129.     kDITop        = $0050;
  130.     kDILeft        = $0070;
  131.  
  132. { Phase 2 stuff }
  133.     xCall                = 246;            { csCode for Phase 2 calls to .XPP }
  134.     zipGetLocalZones     = 5;            { .XPP subcodes }
  135.     zipGetZoneList        = 6;
  136.     zipGetMyZone         = 7;
  137.  
  138. VAR
  139.     gMac                : SysEnvRec;    {set up by Initialize}
  140.     gHasWaitNextEvent    : BOOLEAN;        {set up by Initialize}
  141.     gInBackground        : BOOLEAN;        {maintained by Initialize and DoEvent}
  142.  
  143.     gList                : ListHandle;    {the list to be filled with zone names}
  144.  
  145.  
  146.  
  147. {$S Initialize}
  148. FUNCTION TrapAvailable(tNumber: INTEGER; tType: TrapType): BOOLEAN;
  149.  
  150. {Check to see if a given trap is implemented. This is only used by the
  151.  Initialize routine in this program, so we put it in the Initialize segment.
  152.  The recommended approach to see if a trap is implemented is to see if
  153.  the address of the trap routine is the same as the address of the
  154.  Unimplemented trap.}
  155. {1.02 - Needs to be called after call to SysEnvirons so that it can check
  156.  if a ToolTrap is out of range of a pre-MacII ROM.}
  157.  
  158. BEGIN
  159.     IF (tType = ToolTrap) &
  160.         (gMac.machineType > envMachUnknown) &
  161.         (gMac.machineType < envMacII) THEN BEGIN        {it's a 512KE, Plus, or SE}
  162.         tNumber := BAND(tNumber, $03FF);
  163.         IF tNumber > $01FF THEN                            {which means the tool traps}
  164.             tNumber := _Unimplemented;                    {only go to $01FF}
  165.     END;
  166.     TrapAvailable := NGetTrapAddress(tNumber, tType) <>
  167.                         GetTrapAddress(_Unimplemented);
  168. END; {TrapAvailable}
  169.  
  170.  
  171. {$S Main}
  172. PROCEDURE FailOSErrMsg(result, message: INTEGER);
  173. BEGIN
  174.     IF result <> noErr THEN
  175.         Failure(result, message);
  176. END; {SignalOSErrMsg}
  177.  
  178.  
  179. {$S Main}
  180. PROCEDURE FailNILMsg(p: UNIV Ptr; message: INTEGER);
  181. BEGIN
  182.     IF p = NIL THEN
  183.         Failure(memFullErr, message);
  184. END; {FailNILMsg}
  185.  
  186.  
  187. {$S Main}
  188. PROCEDURE AlertUser(error: INTEGER; message: LongInt);
  189.  
  190. {Display an alert to inform the user of an error. Message acts as an
  191.  index into a STR# resource of error messages. If no message is given,
  192.  i.e. = 0, then use a standard message. If error is not noErr then
  193.  display it as well.}
  194.  
  195. VAR
  196.     msg1, msg2    : Str255;
  197.     itemHit        : INTEGER;
  198. BEGIN
  199.     IF message <= 0 THEN message := eStandardErr;
  200.     GetIndString(msg1, sErrStrings, message);
  201.     IF error = noErr THEN
  202.         msg2 := ''
  203.     ELSE
  204.         NumToString(error, msg2);
  205.     ParamText(msg1, msg2, '', '');
  206.     itemHit := Alert(rUserAlert, NIL);
  207. END; {AlertUser}
  208.  
  209.  
  210. {$S Main}
  211. FUNCTION IsDAWindow(window: WindowPtr): BOOLEAN;
  212. BEGIN
  213.     IF window = NIL THEN
  214.         IsDAWindow := FALSE
  215.     ELSE    {DA windows have negative windowKinds}
  216.         IsDAWindow := WindowPeek(window)^.windowKind < 0;
  217. END; {IsDAWindow}
  218.  
  219.  
  220. {$S Main}
  221. FUNCTION IsAppWindow(window: WindowPtr): BOOLEAN;
  222. BEGIN
  223.     IF window = NIL THEN
  224.         IsAppWindow := FALSE
  225.     ELSE    {application windows have windowKinds >= userKind (8) or dialogKind (2)}
  226.         WITH WindowPeek(window)^ DO
  227.             IsAppWindow := (windowKind >= userKind) | (windowKind = dialogKind);
  228. END; {IsAppWindow}
  229.  
  230.  
  231. {$S Main}
  232. PROCEDURE BuildZoneList;
  233.  
  234. {Create the list of zones on the network. Find a bridge to talk to , if one is
  235.  present, then ask it for zone names. Add the names to the list in the dialog.}
  236.  
  237. VAR
  238.     dATPPBptr                    : ATPPBptr;        {the parameter block for GetZoneList call}
  239.     dBDS                        : BDSElement;    {the BDS for GetZoneList call}
  240.     dZones, dCurr                : Ptr;            {the data buffer for GetZoneList call}
  241.     dIndex, dCount, dNode, dNet    : INTEGER;
  242.     ignore                        : INTEGER;
  243.     cSize                        : Point;
  244.     fi                            : FailInfo;
  245.     nodeNetAddress, bridgeNode    : INTEGER;
  246.  
  247.     PROCEDURE CleanUp;
  248.     BEGIN
  249.         IF dATPPBptr <> NIL THEN
  250.             DisposPtr(Ptr(dATPPBptr));                    {get rid of pb block}
  251.         IF dZones <> NIL THEN
  252.             DisposPtr(dZones);                            {and buffer}
  253.     END; {CleanUp}
  254.  
  255.     PROCEDURE HandleErr(error: INTEGER; message: LongInt);
  256.     BEGIN
  257.         CleanUp;                                        {get rid of allocated junk}
  258.     END;
  259.  
  260. BEGIN
  261.     dATPPBptr := NIL;                                    {init some important variables}
  262.     dZones := NIL;
  263.     CatchFailures(fi, HandleErr);
  264.  
  265.     { get network address of node & node ID of bridge (if any) }
  266.     FailOSErrMsg(GetNodeAddress(ignore, nodeNetAddress), eAppleTalk);
  267.     bridgeNode := GetBridgeAddress;
  268.  
  269.     { test to see if bridge node fails.  If so, no internet. }
  270.     if (bridgeNode = 0) then
  271.         Failure(0, eNoZones);                                    { bail if no zones present }
  272.  
  273.     dATPPBptr := ATPPBptr(NewPtr(SIZEOF(ATPParamBlock)));
  274.     FailNILMsg(dATPPBptr, eNoMemory);
  275.     dZones := NewPtr(kZonesSize);
  276.     FailNILMsg(dZones, eNoMemory);
  277.     WITH dBDS DO BEGIN                                    {set up BDS}
  278.         buffSize := kZonesSize;
  279.         buffPtr := dZones;
  280.         END;
  281.     WITH dATPPBptr^ DO BEGIN                            {set up pb block}
  282.         atpFlags := 0;
  283.  
  284.         addrBlock.aNet := nodeNetAddress;
  285.         addrBlock.aNode := bridgeNode;                    {get node of bridge}
  286.         addrBlock.aSocket := kZIPSocket;                {the socket we want}
  287.         reqLength := 0;
  288.         reqPointer := NIL;
  289.         bdsPointer := @dBDS;
  290.         numOfBuffs := 1;
  291.         timeOutVal := kATPTimeOutVal;
  292.         retryCount := kATPRetryCount;
  293.         END;
  294.     dIndex := 1;
  295.     dCount := 0;
  296.     SetPt(cSize, 0, 0);                                    {we always stuff into first}
  297.     REPEAT
  298.         dATPPBptr^.userData := kGZLCall + dIndex;        {indicate GetZoneList request}
  299.         FailOSErrMsg(PSendRequest(dATPPBptr,
  300.             FALSE), eAppleTalk);                        {send sync request}
  301.         dCount := dCount + BAND(dBDS.userBytes,
  302.                     kZoneCount);                        {find out how many returned}
  303.         dCurr := dZones;                                {put current pointer at start}
  304.         REPEAT                                            {get each zone}
  305.             ignore := LAddRow(1, 0, gList);                {create new cell at start}
  306.             LSetCell(POINTER(ORD4(dCurr) + 1), dCurr^,
  307.                         cSize, gList);                    {stuff in zone}
  308.             dCurr := POINTER(ORD4(dCurr) + dCurr^+1);    {bump up current pointer}
  309.             dIndex := dIndex + 1;                        {increment which zone}
  310.         UNTIL dIndex > dCount;
  311.     UNTIL (BAND(dBDS.userBytes, kMoreZones) <> 0);        {keep going until none left}
  312.     CleanUp;
  313.  
  314.     Success(fi);
  315. END; {BuildZoneList}
  316.  
  317.  
  318. {$S Main}
  319. PROCEDURE BuildZoneListPhase2;
  320.  
  321. {Create the list of zones on the network. Find a bridge to talk to , if one is
  322.  present, then ask it for zone names. Add the names to the list in the dialog.}
  323.  
  324. VAR
  325.     dXPBPBPtr                    : xCallParamBlkPtr;
  326.     dBDS                        : BDSElement;    {the BDS for GetZoneList call}
  327.     dZones, dCurr                : Ptr;            {the data buffer for GetZoneList call}
  328.     dIndex, dCount, dNode, dNet    : INTEGER;
  329.     ignore                        : INTEGER;
  330.     cSize                        : Point;
  331.     fi                            : FailInfo;
  332.     xppDriverRefNum                : INTEGER;
  333.     nodeNetAddress, bridgeNode    : INTEGER;
  334.  
  335.     PROCEDURE CleanUp;
  336.     BEGIN
  337.         IF dXPBPBPtr <> NIL THEN
  338.             DisposPtr(Ptr(dXPBPBPtr));                    {get rid of pb block}
  339.         IF dZones <> NIL THEN
  340.             DisposPtr(dZones);                            {and buffer}
  341.     END; {CleanUp}
  342.  
  343.     PROCEDURE HandleErr(error: INTEGER; message: LongInt);
  344.     BEGIN
  345.         CleanUp;                                        {get rid of allocated junk}
  346.     END;
  347.  
  348. BEGIN
  349.     dXPBPBPtr := NIL;                                    {init some important variables}
  350.     dZones := NIL;
  351.     CatchFailures(fi, HandleErr);
  352.  
  353.     { get network address of node & node ID of bridge (if any) }
  354.     FailOSErrMsg(GetNodeAddress(ignore, nodeNetAddress), eAppleTalk);
  355.     bridgeNode := GetBridgeAddress;
  356.  
  357.     { test to see if either one fails.  If so, no internet. }
  358.     if (nodeNetAddress = 0) or (bridgeNode = 0) then
  359.         Failure(0, eNoZones);                                    { bail if no zones present }
  360.  
  361.     { get a hold of the XPP driver reference number-this is the safest way }
  362.     FailOSErrMsg(OpenDriver('.XPP', xppDriverRefNum), eAppleTalk);
  363.  
  364.     dXPBPBPtr := xCallParamBlkPtr(NewPtr(SIZEOF(xCallParam)));
  365.     FailNILMsg(dXPBPBPtr, eNoMemory);
  366.     dZones := NewPtr(kZonesSize);
  367.     FailNILMsg(dZones, eNoMemory);
  368.  
  369.     dXPBPBPtr^.zipInfoField[1] := 0;    { ALWAYS 0 on first call.  has state info on subsequent calls }
  370.     dXPBPBPtr^.zipInfoField[2] := 0;    { ALWAYS 0 on first call.  has state info on subsequent calls }
  371.     dXPBPBPtr^.zipLastFlag := 0;
  372.  
  373.     dXPBPBPtr^.ioRefNum := xppDriverRefNum;
  374.     dXPBPBPtr^.csCode := xCall;
  375.     dXPBPBPtr^.xppSubCode := zipGetZoneList;
  376.     dXPBPBPtr^.xppTimeOut := kATPTimeOutVal;
  377.     dXPBPBPtr^.xppRetry := kATPRetryCount;
  378.     dXPBPBPtr^.zipBuffPtr := Ptr( dZones);
  379.  
  380.     dIndex := 1;
  381.     dCount := 0;
  382.     SetPt(cSize, 0, 0);                                    {we always stuff into first}
  383.     REPEAT
  384.         FailOSErrMsg(PBControl(ParmBlkPtr (dXPBPBPtr), false), eAppleTalk);        { send sync control call }
  385.         dCount := dCount + dXPBPBPtr^.zipNumZones;                        { find out how many returned }
  386.  
  387.         dCurr := dZones;                                {put current pointer at start}
  388.         REPEAT                                            {get each zone}
  389.             ignore := LAddRow(1, 0, gList);                {create new cell at start}
  390.             LSetCell(POINTER(ORD4(dCurr) + 1), dCurr^,
  391.                 cSize, gList);                            {stuff in zone}
  392.             dCurr := POINTER(ORD4(dCurr) + dCurr^+1);    {bump up current pointer}
  393.             dIndex := dIndex + 1;                        {increment which zone}
  394.         UNTIL dIndex > dCount;
  395.     UNTIL (dXPBPBPtr^.zipLastFlag <> 0);        {keep going until none left}
  396.     CleanUp;
  397.  
  398.     Success(fi);
  399. END; {BuildZoneListPhase2}
  400.  
  401.  
  402. {$S Main}
  403. PROCEDURE ZoneListDraw(dlg: DialogPtr; item: INTEGER);
  404.  
  405. {The user item procedure for the zone list user item and default
  406.  box user item in the dialog. Draw the list and the frame that goes with it.
  407.  Draw the default box around the OK button.}
  408.  
  409. VAR
  410.     port    : GrafPtr;
  411.     kind    : INTEGER;
  412.     h        : Handle;
  413.     r        : Rect;
  414.     ps        : PenState;
  415.  
  416. BEGIN
  417.     GetPort(port);                                    {save old port}
  418.     SetPort(dlg);                                    {make dialog port}
  419.     CASE item OF
  420.     dZoneList: BEGIN
  421.             LUpdate(dlg^.visRgn, gList);            {re-draw list}
  422.             GetDItem(dlg, dZoneList, kind, h, r);
  423.             InsetRect(r, kListInset, kListInset);
  424.             FrameRect(r);                            {re-draw frame}
  425.         END;
  426.     dDefault: BEGIN
  427.             GetDItem(dlg, dDefault, kind, h, r);
  428.             GetPenState(ps);
  429.             PenNormal;                                {always be on the defensive}
  430.             PenSize(3, 3);
  431.             InsetRect(r, -4, -4);
  432.             FrameRoundRect(r, 16, 16);                {draw default box}
  433.             SetPenState(ps);
  434.         END;
  435.     END;
  436.     SetPort(port);                                    {restore old port}
  437. END; {ZoneListDraw}
  438.  
  439.  
  440. {$S Main}
  441. FUNCTION ListFilter (dlg: DialogPtr; VAR event: EventRecord;
  442.                                             VAR item: INTEGER) : BOOLEAN;
  443.  
  444. {Passed as parameter to ModalDialog. Handle key presses and mouse clicks
  445.  from the user. Do all the right default actions since we override them
  446.  by virtue of our existence.}
  447.  
  448. VAR
  449.     port        : GrafPtr;
  450.     loc            : Point;
  451.     kind        : INTEGER;
  452.     h            : Handle;
  453.     r            : Rect;
  454.     ignore        : BOOLEAN;
  455.     key            : SignedByte;
  456.     finalTicks    : LongInt;
  457. BEGIN
  458.     ListFilter := FALSE;                                {always default FALSE}
  459.     CASE event.what OF
  460.         keyDown, autoKey: BEGIN                            {check for <cr> or <enter>}
  461.             key := SignedByte(event.message);
  462.             IF key IN [kCR, kENTER] THEN BEGIN            {it was a <cr> or <enter>}
  463.                 GetDItem(dlg, ok, kind, h, r);
  464.                 HiliteControl(ControlHandle(h), kHilite);
  465.                 Delay(kHiliteDelay, finalTicks);
  466.                 HiliteControl(ControlHandle(h), kDeHilite);
  467.                 ListFilter := TRUE;                        {so we handle it}
  468.                 item := 1;                                {and make the first item hit}
  469.             END;
  470.         END;
  471.         mouseDown: BEGIN                                {we want mouseDowns}
  472.             GetPort(port);
  473.             SetPort(dlg);
  474.             loc := event.where;
  475.             GlobalToLocal(loc);                            {find where clicked}
  476.             GetDItem(dlg, dZoneList, kind, h, r);        {get rect for list}
  477.             IF PtInRect(loc, r) THEN BEGIN                {if clicked inside…}
  478.                 ListFilter := TRUE;                        {we take care of it}
  479.                 ignore := LClick(loc, event.modifiers,
  480.                                     gList);                {by passing click to list}
  481.             END;
  482.             SetPort(port);
  483.         END;
  484.     END;
  485. END; {ListFilter}
  486.  
  487.  
  488. {$S Main}
  489. PROCEDURE DoZoneList;
  490.  
  491. {Put up a modal dialog that shows a list of the zones on the net. Create the dialog
  492.  and list, call BuildZoneList to fill it, then wait for the user to click OK.}
  493.  
  494. VAR
  495.     dlg                        : DialogPtr;
  496.     item, kind                : INTEGER;
  497.     h                        : Handle;
  498.     r, rView, dataBounds    : Rect;
  499.     cSize                    : Point;
  500.     fi                        : FailInfo;
  501.     hor, ver                : INTEGER;
  502.  
  503.     PROCEDURE CleanUp;
  504.     BEGIN
  505.         IF gList <> NIL THEN
  506.             LDispose(gList);                                {get rid of list}
  507.         IF dlg <> NIL THEN
  508.             DisposDialog(dlg);                                {get rid of dialog}
  509.     END; {CleanUp}
  510.  
  511.     PROCEDURE HandleErr(error: INTEGER; message: LongInt);
  512.     BEGIN
  513.         CleanUp;                                            {release junk}
  514.     END;
  515.  
  516. BEGIN
  517.     gList := NIL;                                            {init some important variables}
  518.     dlg := NIL;
  519.     CatchFailures(fi, HandleErr);
  520.  
  521.     dlg := GetNewDialog(rZoneDialog, NIL, POINTER(-1));        {create dialog}
  522.     FailNILMsg(dlg, eNoMemory);
  523.  
  524.     {We center the dialog horizontally and position it vertically one-third the
  525.      distance from the menu bar to the bottom of the main device. We do not
  526.      check for the dialog extending past the bottom of the device because we
  527.      know the dialog is not that big. You may wish to make that check.}
  528.     WITH dlg^.portRect DO BEGIN
  529.         hor := right - left;
  530.         ver := bottom - top;
  531.     END;
  532.     WITH screenBits.bounds DO BEGIN
  533.         hor := ((right - left) - hor) DIV 2;
  534.         ver := (((bottom - top) - ver - GetMBarHeight) DIV 3) + GetMBarHeight;
  535.     END;
  536.     MoveWindow(dlg, hor, ver, FALSE);
  537.  
  538.     GetDItem(dlg, dDefault, kind, h, r);
  539.     h := @ZoneListDraw;                                        {connect drawing procedure}
  540.     SetDItem(dlg, dDefault, kind, h, r);
  541.     GetDItem(dlg, dZoneList, kind, h, r);
  542.     h := @ZoneListDraw;                                        {connect drawing procedure}
  543.     SetDItem(dlg, dZoneList, kind, h, r);
  544.     rView := r;
  545.     WITH rView DO
  546.         right := right - kScrollBarWidth;                    {adjust rectangle for scroll}
  547.     SetRect(dataBounds, 0, 0, 1, 0);                        {init to one-wide list}
  548.     SetPt(cSize, 0, 0);
  549.     gList := LNew(rView, dataBounds, cSize, 0, WindowPtr(dlg),
  550.                     FALSE, FALSE, FALSE, TRUE);                {create with vertical scroll}
  551.     FailNILMsg(gList, eNoMemory);
  552.  
  553.     if gMac.atDrvrVersNum >= 53 then
  554.         BuildZoneListPhase2
  555.     else
  556.         BuildZoneList;                                            {put the stuff into the list}
  557.  
  558.     SetPt(cSize, 0, 0);
  559.     LSetSelect(TRUE, cSize, gList);                            {select the first guy}
  560.     LDoDraw(TRUE, gList);                                    {turn on the list}
  561.     ShowWindow(dlg);                                        {turn on the dialog}
  562.     REPEAT
  563.         ModalDialog(@ListFilter, item);                        {accept events}
  564.     UNTIL item = ok;                                        {until he presses 'ok'}
  565.     CleanUp;
  566.  
  567.     Success(fi);
  568. END; {DoZoneList}
  569.  
  570.  
  571. {$S Main}
  572. FUNCTION DoCloseWindow(window: WindowPtr) : BOOLEAN;
  573. BEGIN
  574.     DoCloseWindow := TRUE;
  575.     IF IsDAWindow(window) THEN
  576.         CloseDeskAcc(WindowPeek(window)^.windowKind);
  577.     IF IsAppWindow(window) THEN
  578.         CloseWindow(window);
  579. END; {DoCloseWindow}
  580.  
  581.  
  582. {$S Initialize}
  583. PROCEDURE Initialize;
  584. VAR
  585.     menuBar            : Handle;
  586.     window            : WindowPtr;
  587.     ignoreError        : OSErr;
  588.     total, contig    : LongInt;
  589.     ignoreResult    : BOOLEAN;
  590.     event            : EventRecord;
  591.     count            : INTEGER;
  592.     fi                : FailInfo;
  593.  
  594.     PROCEDURE HandleErr(error: INTEGER; message: LongInt);
  595.     BEGIN
  596.         IF error > 0 THEN
  597.             AlertUser(0, error)
  598.         ELSE
  599.             AlertUser(error, message);
  600.         ExitToShell;
  601.     END; {HandleErr}
  602.  
  603. BEGIN
  604.     gHasWaitNextEvent := TrapAvailable(_WaitNextEvent, ToolTrap);
  605.     gInBackground := FALSE;
  606.  
  607.     InitGraf(@thePort);
  608.     InitFonts;
  609.     InitWindows;
  610.     InitMenus;
  611.     TEInit;
  612.     InitDialogs(NIL);
  613.     InitCursor;
  614.  
  615.     FOR count := 1 TO 3 DO
  616.         ignoreResult := EventAvail(everyEvent, event);
  617.  
  618.     CatchFailures(fi, HandleErr);
  619.  
  620.     FailOSErrMsg(MPPOpen, eAppleTalk);
  621.     FailOSErrMsg(ATPLoad, eAppleTalk);
  622.  
  623.     ignoreError := SysEnvirons(kSysEnvironsVersion, gMac);
  624.     IF gMac.machineType < 0 THEN
  625.         Failure(0, eWrongMachine);
  626.  
  627.     IF ORD(GetApplLimit) - ORD(ApplicZone) < kMinHeap THEN
  628.         Failure(0, eSmallSize);
  629.  
  630.     PurgeSpace(total, contig);
  631.     IF total < kMinSpace THEN
  632.         Failure(0, eNoMemory);
  633.  
  634.     menuBar := GetNewMBar(rMenuBar);        {read menus into menu bar}
  635.     FailNILMsg(menuBar, eNoMemory);
  636.     SetMenuBar(menuBar);                    {install menus}
  637.     DisposHandle(menuBar);
  638.     AddResMenu(GetMHandle(mApple), 'DRVR');    {add DA names to Apple menu}
  639.     DrawMenuBar;
  640.  
  641.     Success(fi);
  642. END; {Initialize}
  643.  
  644.  
  645. {$S Main}
  646. PROCEDURE Terminate;
  647. VAR
  648.     aWindow    : WindowPtr;
  649.     closed    : BOOLEAN;
  650.  
  651. BEGIN
  652.     closed := TRUE;
  653.     REPEAT
  654.         aWindow := FrontWindow;                    {get the current front window}
  655.         IF aWindow <> NIL THEN
  656.             closed := DoCloseWindow(aWindow);    {close this window}
  657.     UNTIL (NOT closed) | (aWindow = NIL);        {do all windows}
  658.     IF closed THEN
  659.         ExitToShell;                            {exit if no cancellation}
  660. END; {Terminate}
  661.  
  662.  
  663. {$S Main}
  664. PROCEDURE AdjustMenus;
  665. VAR
  666.     window            : WindowPtr;
  667.     menu            : MenuHandle;
  668.  
  669. BEGIN
  670.     window := FrontWindow;
  671.  
  672.     menu := GetMHandle(mFile);
  673.     IF IsDAWindow(window) THEN                {we can allow desk accessories to be closed from the menu}
  674.         EnableItem(menu, iClose)
  675.     ELSE
  676.         DisableItem(menu, iClose);            {but not our traffic light window}
  677.  
  678.     menu := GetMHandle(mEdit);
  679.     IF IsDAWindow(window) THEN BEGIN        {a desk accessory might need the edit menu}
  680.         EnableItem(menu, iUndo);
  681.         EnableItem(menu, iCut);
  682.         EnableItem(menu, iCopy);
  683.         EnableItem(menu, iPaste);
  684.         EnableItem(menu, iClear);
  685.     END ELSE BEGIN                            {but we know we do not}
  686.         DisableItem(menu, iUndo);
  687.         DisableItem(menu, iCut);
  688.         DisableItem(menu, iCopy);
  689.         DisableItem(menu, iClear);
  690.         DisableItem(menu, iPaste);
  691.     END;
  692.  
  693. END; {AdjustMenus}
  694.  
  695.  
  696. {$S Main}
  697. PROCEDURE DoMenuCommand(menuResult: LONGINT);
  698. VAR
  699.     menuID            : INTEGER;        {the resource ID of the selected menu}
  700.     menuItem        : INTEGER;        {the item number of the selected menu}
  701.     itemHit            : INTEGER;
  702.     daName            : Str255;
  703.     daRefNum        : INTEGER;
  704.     handledByDA        : BOOLEAN;
  705.     ignore            : BOOLEAN;
  706.     fi                : FailInfo;
  707.  
  708.     PROCEDURE HandleMenu(error: INTEGER; message: LongInt);
  709.     BEGIN
  710.         HiliteMenu(0);                {unhighlight what MenuSelect (or MenuKey) hilited}
  711.     END;
  712.  
  713. BEGIN
  714.     CatchFailures(fi, HandleMenu);
  715.     menuID := HiWrd(menuResult);    {use built-ins (for efficiency)...}
  716.     menuItem := LoWrd(menuResult);    {to get menu item number and menu number}
  717.     CASE menuID OF
  718.         mApple:
  719.             CASE menuItem OF
  720.                 iAbout:                {bring up alert for About}
  721.                     itemHit := Alert(rAboutAlert, NIL);
  722.                 OTHERWISE BEGIN        {all non-About items in this menu are DAs}
  723.                     GetItem(GetMHandle(mApple), menuItem, daName);
  724.                     daRefNum := OpenDeskAcc(daName);
  725.                 END;
  726.             END;
  727.         mFile:
  728.             CASE menuItem OF
  729.                 iNew:
  730.                     DoZoneList;
  731.                 iClose:
  732.                     ignore := DoCloseWindow(FrontWindow);
  733.                 iQuit:
  734.                     Terminate;
  735.             END;
  736.         mEdit:                        {call SystemEdit for DA editing & MultiFinder}
  737.             handledByDA := SystemEdit(menuItem-1);    {since we don't do any editing}
  738.     END;
  739.     Success(fi);
  740.     HiliteMenu(0);                    {cleanup}
  741. END; {DoMenuCommand}
  742.  
  743.  
  744. {$S Main}
  745. PROCEDURE DoEvent(event: EventRecord);
  746. VAR
  747.     part, err    : INTEGER;
  748.     window        : WindowPtr;
  749.     hit            : BOOLEAN;
  750.     key            : CHAR;
  751.     fi            : FailInfo;
  752.     aPoint        : Point;
  753.  
  754.     PROCEDURE HandleErr(error: INTEGER; message: LongInt);
  755.     BEGIN
  756.         IF error > 0 THEN
  757.             AlertUser(0, error)
  758.         ELSE
  759.             AlertUser(error, message);
  760.         EXIT(DoEvent);
  761.     END; {HandleErr}
  762.  
  763. BEGIN
  764.     CatchFailures(fi, HandleErr);
  765.  
  766.     CASE event.what OF
  767.         mouseDown: BEGIN
  768.             part := FindWindow(event.where, window);
  769.             CASE part OF
  770.                 inMenuBar: BEGIN            {process the menu command}
  771.                     AdjustMenus;
  772.                     DoMenuCommand(MenuSelect(event.where));
  773.                 END;
  774.                 inSysWindow:                {let the system handle the mouseDown}
  775.                     SystemClick(event, window);
  776.                 inContent:;
  777.                 inDrag:;
  778.                 inGrow:;
  779.                 inZoomIn, inZoomOut:;
  780.             END;
  781.         END;
  782.         keyDown, autoKey: BEGIN                {check for menukey equivalents}
  783.             key := CHR(BAnd(event.message, charCodeMask));
  784.             IF BAnd(event.modifiers, cmdKey) <> 0 THEN    {Command key down}
  785.                 IF event.what = keyDown THEN BEGIN
  786.                     AdjustMenus;            {enable/disable/check menu items properly}
  787.                     DoMenuCommand(MenuKey(key));
  788.                 END;
  789.         END;                                {call DoActivate with the window and...}
  790.         activateEvt:;
  791.         updateEvt:;
  792.         {1.01 - It is not a bad idea to at least call DIBadMount in response
  793.          to a diskEvt, so that the user can format a floppy.}
  794.         diskEvt:
  795.             IF HiWrd(event.message) <> noErr THEN BEGIN
  796.                 SetPt(aPoint, kDILeft, kDITop);
  797.                 err := DIBadMount(aPoint, event.message);
  798.             END;
  799.         kOSEvent:
  800.             CASE BAnd(BRotL(event.message, 8), 255) OF    {high byte of message}
  801.                 kSuspendResumeMessage: BEGIN
  802.                     gInBackground := BAnd(event.message, kResumeMask) = 0;
  803.                 END;
  804.             END;
  805.     END;
  806.  
  807.     Success(fi);
  808. END; {DoEvent}
  809.  
  810.  
  811. {$S Main}
  812. PROCEDURE EventLoop;
  813. VAR
  814.     cursorRgn    : RgnHandle;
  815.     gotEvent    : BOOLEAN;
  816.     event        : EventRecord;
  817.  
  818. BEGIN
  819.     cursorRgn := NewRgn;            {we’ll pass WNE an empty region the 1st time thru}
  820.  
  821.     REPEAT
  822.         IF gHasWaitNextEvent THEN    {put us 'asleep' forever under MultiFinder}
  823.             gotEvent := WaitNextEvent(everyEvent, event, MAXLONGINT, cursorRgn)
  824.         ELSE BEGIN
  825.             SystemTask;                {must be called if using GetNextEvent}
  826.             gotEvent := GetNextEvent(everyEvent, event);
  827.         END;
  828.         IF gotEvent THEN BEGIN
  829.             DoEvent(event);
  830.         END;
  831.     UNTIL FALSE;                    {loop forever; we quit through an ExitToShell}
  832. END; {EventLoop}
  833.  
  834.  
  835. PROCEDURE _DataInit; EXTERNAL;
  836.  
  837.  
  838. {$S Main}
  839. BEGIN
  840.     UnloadSeg(@_DataInit);        {note that _DataInit must not be in Main!}
  841.     MaxApplZone;                {expand the heap so code segments load at the top}
  842.  
  843.     InitSignals;
  844.     Initialize;                    {initialize the program}
  845.     UnloadSeg(@Initialize);        {note that Initialize must not be in Main!}
  846.  
  847.     EventLoop;                    {call the main event loop}
  848. END.
  849.